fingerprint: Recover from fs::stat errors
authorTom Jakubowski <tom@crystae.net>
Mon, 7 Jul 2014 21:46:03 +0000 (14:46 -0700)
committerTom Jakubowski <tom@crystae.net>
Mon, 7 Jul 2014 22:33:40 +0000 (15:33 -0700)
Fingerprinting will fail at an fs::stat() call if there is a broken
symlink in a package's directory.  This commit recovers from fs::stat()
errors on broken symlinks by treating them as having mtime 0, which
should not affect the overall fingerprint.

Fix #135

src/cargo/sources/path.rs
tests/support/mod.rs
tests/test_cargo_compile.rs

index 26baccd57201ef0cc701233f4e3cd423cc0a2eaf..e5a2a5757e7937c9af0894f126a593ec3c85be73 100644 (file)
@@ -106,7 +106,12 @@ impl Source for PathSource {
 
         fn walk(path: &Path, is_root: bool) -> CargoResult<u64> {
             if !path.is_dir() {
-                return Ok(try!(fs::stat(path)).modified)
+                // An fs::stat error here is either because path is a
+                // broken symlink, a permissions error, or a race
+                // condition where this path was rm'ed - either way,
+                // we can ignore the error and treat the path's mtime
+                // as 0.
+                return Ok(fs::stat(path).map(|s| s.modified).unwrap_or(0))
             }
             // Don't recurse into any sub-packages that we have
             if !is_root && path.join("Cargo.toml").exists() { return Ok(0) }
index 7cd0faa4ac92328494a5158547fb7102470541c6..49ab97f93a58e5f0d40e7a68e8c35e233b0dea97 100644 (file)
@@ -51,11 +51,36 @@ impl FileBuilder {
     }
 }
 
+#[deriving(PartialEq,Clone)]
+struct SymlinkBuilder {
+    dst: Path,
+    src: Path
+}
+
+impl SymlinkBuilder {
+    pub fn new(dst: Path, src: Path) -> SymlinkBuilder {
+        SymlinkBuilder { dst: dst, src: src }
+    }
+
+    fn mk(&self) -> Result<(), String> {
+        try!(mkdir_recursive(&self.dirname()));
+
+        fs::symlink(&self.dst, &self.src)
+            .with_err_msg(format!("Could not create symlink; dst={} src={}",
+                                   self.dst.display(), self.src.display()))
+    }
+
+    fn dirname(&self) -> Path {
+        Path::new(self.src.dirname())
+    }
+}
+
 #[deriving(PartialEq,Clone)]
 pub struct ProjectBuilder {
     name: String,
     root: Path,
-    files: Vec<FileBuilder>
+    files: Vec<FileBuilder>,
+    symlinks: Vec<SymlinkBuilder>
 }
 
 impl ProjectBuilder {
@@ -63,7 +88,8 @@ impl ProjectBuilder {
         ProjectBuilder {
             name: name.to_str(),
             root: root,
-            files: vec!()
+            files: vec!(),
+            symlinks: vec!()
         }
     }
 
@@ -93,6 +119,13 @@ impl ProjectBuilder {
         self
     }
 
+    pub fn symlink<T: BytesContainer>(mut self, dst: T,
+                                      src: T) -> ProjectBuilder {
+        self.symlinks.push(SymlinkBuilder::new(self.root.join(dst),
+                                               self.root.join(src)));
+        self
+    }
+
     // TODO: return something different than a ProjectBuilder
     pub fn build<'a>(&'a self) -> &'a ProjectBuilder {
         match self.build_with_result() {
@@ -112,6 +145,10 @@ impl ProjectBuilder {
             try!(file.mk());
         }
 
+        for symlink in self.symlinks.iter() {
+            try!(symlink.mk());
+        }
+
         Ok(())
     }
 
index 9fa6a8aa8b1b0e4e29014fc8a4ebb232d2fc7067..771988eb3aea3ca2a43bc5391c217484accf0560 100644 (file)
@@ -626,3 +626,17 @@ test!(self_dependency {
     assert_that(p.cargo_process("cargo-build"),
                 execs().with_status(0));
 })
+
+test!(ignore_bogus_symlinks {
+    let p = project("foo")
+        .file("Cargo.toml", basic_bin_manifest("foo").as_slice())
+        .file("src/foo.rs", main_file(r#""i am foo""#, []).as_slice())
+        .symlink("Notafile", "bar");
+
+    assert_that(p.cargo_process("cargo-build"), execs());
+    assert_that(&p.bin("foo"), existing_file());
+
+    assert_that(
+      process(p.bin("foo")),
+      execs().with_stdout("i am foo\n"));
+})